package com.yummy.cloud.gateway.route;

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.SmartLifecycle;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

import com.yummy.cloud.gateway.config.RouteWatchProperties;

public class GatewayRouteWatch implements ApplicationEventPublisherAware , SmartLifecycle{
	
	private ApplicationEventPublisher applicationEventPublisher;
	private final TaskScheduler taskScheduler;
	private final AtomicBoolean running = new AtomicBoolean(false);
	private ScheduledFuture<?> watchFuture;
	private final AtomicLong routeWatchIndex = new AtomicLong(0);
	private RouteWatchProperties properties;

	public GatewayRouteWatch(RouteWatchProperties properties,ObjectProvider<TaskScheduler> taskScheduler) {
		this.properties = properties;
		this.taskScheduler = taskScheduler.getIfAvailable(GatewayRouteWatch::getTaskScheduler);
	}
	
	private static ThreadPoolTaskScheduler getTaskScheduler() {
		ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
		taskScheduler.setBeanName("Route-Watch-Task-Scheduler");
		taskScheduler.initialize();
		return taskScheduler;
	}
	
	@Override
	public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
		this.applicationEventPublisher = applicationEventPublisher;
	}
	
	public void routeWatch() {
		applicationEventPublisher.publishEvent(new HeartbeatEvent(this,routeWatchIndex.getAndIncrement()));
	}

	@Override
	public void start() {
		if (this.running.compareAndSet(false, true)) {
			this.watchFuture = this.taskScheduler.scheduleWithFixedDelay(
					this::routeWatch, this.properties.getWatchDelay());
		}
	}

	@Override
	public void stop() {
		if (this.running.compareAndSet(true, false) && this.watchFuture != null) {
			// shutdown current user-thread,
			// then the other daemon-threads will terminate automatic.
			((ThreadPoolTaskScheduler) this.taskScheduler).shutdown();

			this.watchFuture.cancel(true);
		}
	}

	@Override
	public boolean isRunning() {
		return this.running.get();
	}

}
